using System;
using System.Web;
using Glimpse.AspNet;
using Glimpse.Core.Extensibility;
using Glimpse.Core.Framework;

// HACK: We'll open up Glimpse to make the out of the box HttpModule work in this scenario so that this code can be deleted.
// This is essentially a copy and paste of the out of the box HttpModule, simplified with one change detailed below.
public class OrchardHttpModule : IHttpModule
{
    private const string RuntimeKey = "__GlimpseRuntime";
    private const string LoggerKey = "__GlimpseLogger";
    private static readonly object LockObj = new object();
    private static readonly Factory Factory;

    static OrchardHttpModule()
    {
        var serviceLocator = new AspNetServiceLocator();
        Factory = new Factory(serviceLocator);
    }

    public void Init(HttpApplication httpApplication)
    {
        Init(WithTestable(httpApplication));
    }

    public void Dispose()
    {
        // Nothing to dispose
    }

    internal void Init(HttpApplicationBase httpApplication)
    {
        var runtime = GetRuntime(httpApplication.Application);

        AppDomain.CurrentDomain.SetData(LoggerKey, Factory.InstantiateLogger());

        if (runtime.IsInitialized || runtime.Initialize())
        {
            httpApplication.BeginRequest += (context, e) => BeginRequest(WithTestable(context));
            httpApplication.PostAcquireRequestState += (context, e) => BeginSessionAccess(WithTestable(context));
            httpApplication.PostRequestHandlerExecute += (context, e) => EndSessionAccess(WithTestable(context));
            httpApplication.PostReleaseRequestState += (context, e) => EndRequest(WithTestable(context));
        }
    }

    internal IGlimpseRuntime GetRuntime(HttpApplicationStateBase applicationState)
    {
        var runtime = applicationState[RuntimeKey] as IGlimpseRuntime;

        if (runtime == null)
        {
            lock (LockObj)
            {
                runtime = applicationState[RuntimeKey] as IGlimpseRuntime;

                if (runtime == null)
                {
                    // HACK: Due to a current bug in Factory, I'm just overriding the config here before creating the runtime.
                    var config = Factory.InstantiateConfiguration() as GlimpseConfiguration;
                    config.EndpointBaseUri = "~/Glimpse";
                    config.DefaultRuntimePolicy = RuntimePolicy.On;

                    runtime = new GlimpseRuntime(config);

                    applicationState.Add(RuntimeKey, runtime);
                }
            }
        }

        return runtime;
    }

    internal void BeginRequest(HttpContextBase httpContext)
    {
        // TODO: Add Logging to either methods here or in Runtime
        var runtime = GetRuntime(httpContext.Application);

        runtime.BeginRequest();
    }

    internal void EndRequest(HttpContextBase httpContext)
    {
        var runtime = GetRuntime(httpContext.Application);

        runtime.EndRequest();
    }

    private static HttpContextBase WithTestable(object sender)
    {
        var httpApplication = sender as HttpApplication;

        return new HttpContextWrapper(httpApplication.Context);
    }

    private static HttpApplicationBase WithTestable(HttpApplication httpApplication)
    {
        return new HttpApplicationWrapper(httpApplication);
    }

    private void BeginSessionAccess(HttpContextBase httpContext)
    {
        var runtime = GetRuntime(httpContext.Application);

        runtime.BeginSessionAccess();
    }

    private void EndSessionAccess(HttpContextBase httpContext)
    {
        var runtime = GetRuntime(httpContext.Application);

        runtime.EndSessionAccess();
    }
}
